﻿// (c) Copyright Microsoft Corporation.
// This source is subject to the Microsoft Public License (Ms-PL).
// Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.
// All other rights reserved
// Developer:  Michael Antonio
using System;
using System.Threading;

namespace MichMan.Utilities.Requests
{
    /// <summary>
    /// An abstract base class for all ICache implementations
    /// </summary>
    public abstract class CacheBase
    {
        #region ICache
        /// <summary>
        /// Gets the specified key.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="key">The key.</param>
        /// <returns></returns>
        public T Get<T>(String key)
        {
            return Get(key, default(T));
        }

        /// <summary>
        /// Gets the specified key.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="key">The key.</param>
        /// <param name="defaultValue">The default value.</param>
        /// <returns></returns>
        public T Get<T>(string key, T defaultValue)
        {
            object o = ((ICache)this).Get(key);
            if (o == null)
            {
                return defaultValue;
            }
            return (T)o;
        }

        /// <summary>
        /// Inserts the specified key.
        /// </summary>
        /// <param name="key">The key.</param>
        /// <param name="obj">The obj.</param>
        /// <returns></returns>
        public virtual bool Insert(string key, object obj)
        {
            return ((ICache)this).Insert(key, obj, null);
        }
        #endregion

        #region Expiry
        /// <summary>
        /// When the next item will expire.  Return TimeSpan.MaxValue to indicate that no items will expire.
        /// </summary>
        /// <returns></returns>
        protected virtual TimeSpan GetNextExpiry()
        {
            return TimeSpan.MaxValue;
        }

        /// <summary>
        /// Called periodically to remove entries that have expired.
        /// Must be implemented if you are using the Expiry timer.
        /// </summary>
        protected virtual void RemoveExpiredEntries()
        {
        }

        private Timer _timer = null;

        /// <summary>
        /// Must be called from Insert after an item is inserted into the cache, otherwise the timer won't start.
        /// In this manner, use of the timer is optional.
        /// </summary>
        protected void UpdateExpiryTimer()
        {
            TimeSpan expiry = GetNextExpiry();
            if (expiry == TimeSpan.MaxValue)
            {
                if (_timer != null)
                {
                    // Turn the timer off.
                    _timer.Change(Timeout.Infinite, Timeout.Infinite);
                    _timer = null;
                }
                return;
            }

            if (_timer == null)
            {
                _timer = new Timer(new TimerCallback(TimerCallback));
            }
            _timer.Change(expiry >= TimeSpan.FromMinutes(1) ? expiry : TimeSpan.FromMinutes(1), TimeSpan.FromMilliseconds(-1));
        }

        private void TimerCallback(object state)
        {
            RemoveExpiredEntries();
            UpdateExpiryTimer();
        }

        #endregion
    }
}
